/**
 * Copyright (c) [2022] LiuGang(libbylg@126.com)
 * TinyEditor is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *             http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "vm_impl.h"
#include "../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/include/math.h"
#include "vm.h"

#include <cstdlib>
#include <cstring>

extern vm_t* vm_create() {
    vm_t* vm = reinterpret_cast<vm_t*>(malloc(sizeof(vm_t)));
    memset(vm, 0, sizeof(vm_t));
    return vm;
}

extern void vm_destroy(vm_t* vm) {
    ASSERT(vm != nullptr);
    free(vm);
}

extern void vm_load(vm_t* vm, const vm_inst_t* insts, uint32_t insts_num) {
    ASSERT(vm != nullptr);
    ASSERT(insts != nullptr);
    ASSERT(insts_num > 0);
    vm->insts = insts;
    vm->insts_num = insts_num;
}

static uint64_t vm_mem_ld(uint64_t address, size_t size) {
    if ((address % sizeof(uint64_t)) == 0) {
        switch (size) {
            case 1:
                return *reinterpret_cast<uint8_t*>(address);
            case 2:
                return *reinterpret_cast<uint16_t*>(address);
            case 4:
                return *reinterpret_cast<uint32_t*>(address);
            case 8:
                return *reinterpret_cast<uint64_t*>(address);
            default:
                return 0;
        }
    }

    uint64_t value = 0;
    memcpy(&value, reinterpret_cast<void*>(address), size);
    return value;
}

static void vm_mem_st(uint64_t address, uint64_t value, size_t size) {
    if ((address % sizeof(uint64_t)) == 0) {
        switch (size) {
            case 1:
                *reinterpret_cast<uint8_t*>(address) = value;
                break;
            case 2:
                *reinterpret_cast<uint16_t*>(address) = value;
                break;
            case 4:
                *reinterpret_cast<uint32_t*>(address) = value;
                break;
            case 8:
                *reinterpret_cast<uint64_t*>(address) = value;
                break;
            default:
                return;
        }
    }

    memcpy(reinterpret_cast<void*>(address), &value, size);
}

static uint64_t vm_frame_load(vm_t* vm, vm_frame_t* frame) {
    for (int i = 0; i < VM_REG_CALL_NUM; i++) {
        vm->regs[VM_REG_CALL_BEGIN + i] = frame->regs[i];
    }

    return frame->pc;
}

static void vm_frame_save(vm_t* vm, vm_frame_t* frame, uint64_t pc) {
    for (int i = 0; i < VM_REG_CALL_NUM; i++) {
        frame->regs[i] = vm->regs[VM_REG_CALL_BEGIN + i];
    }

    frame->pc = pc;
}

static vm_inst_t vm_fetch_inst(vm_t* vm, uint32_t pc) {
    ASSERT(vm != nullptr);
    if (pc >= vm->insts_num) {
        vm_inst_t inst = {.opcode = VM_OP_FAULT, .target = 0, .source = 0, .offset = 0, .immval = 1};
        return inst;
    }

    return vm->insts[pc];
}

static uint64_t vm_fetch_dword(const vm_t* vm, uint32_t pc) {
    ASSERT(vm != nullptr);
    if (pc >= vm->insts_num) {
        return 0;
    }

    return *reinterpret_cast<const uint64_t*>(vm->insts + pc);
}

extern int vm_exec(vm_t* vm, void* memory, uint32_t size, uint64_t* result) {
    ASSERT(vm != nullptr);
    ASSERT(vm->insts_num > 0);
    ASSERT(vm->insts != nullptr);
    uint64_t* stack = vm->stacks;
    uint64_t* reg = vm->regs;
    double* reg_real = reinterpret_cast<double*>(vm->regs);
    uint64_t pc = 0;
    vm_frame_t* frame = vm->frames;

    while (true) {
        vm_inst_t inst = vm_fetch_inst(vm, pc++);

        switch (inst.opcode) {
            //! 内存数据读写
            case VM_OP_LDB:
                reg[inst.target] = vm_mem_ld(reg[inst.source] + inst.offset, 1);
                break;
            case VM_OP_LDH:
                reg[inst.target] = vm_mem_ld(reg[inst.source] + inst.offset, 2);
                break;
            case VM_OP_LDW:
                reg[inst.target] = vm_mem_ld(reg[inst.source] + inst.offset, 4);
                break;
            case VM_OP_LDD:
                reg[inst.target] = vm_mem_ld(reg[inst.source] + inst.offset, 8);
                break;
            case VM_OP_STB:
                vm_mem_st(reg[inst.target] + inst.offset, reg[inst.source], 1);
                break;
            case VM_OP_STH:
                vm_mem_st(reg[inst.target] + inst.offset, reg[inst.source], 2);
                break;
            case VM_OP_STW:
                vm_mem_st(reg[inst.target] + inst.offset, reg[inst.source], 4);
                break;
            case VM_OP_STD:
                vm_mem_st(reg[inst.target] + inst.offset, reg[inst.source], 8);
                break;
            //! 整数算术运算
            case VM_OP_IADDI:
                reg[inst.target] += static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_IADDR:
                reg[inst.target] += reg[inst.source];
                break;
            case VM_OP_ISUBI:
                reg[inst.target] -= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_ISUBR:
                reg[inst.target] -= reg[inst.source];
                break;
            case VM_OP_IMULI:
                reg[inst.target] *= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_IMULR:
                reg[inst.target] *= reg[inst.source];
                break;
            case VM_OP_IDIVI:
                reg[inst.target] /= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_IDIVR:
                reg[inst.target] /= reg[inst.source];
                break;
            case VM_OP_IMODI:
                reg[inst.target] %= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_IMODR:
                reg[inst.target] %= reg[inst.source];
                break;

            //! 整数位运算
            case VM_OP_BANDI:
                reg[inst.target] &= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_BANDR:
                reg[inst.target] &= reg[inst.source];
                break;
            case VM_OP_BCORI:
                reg[inst.target] |= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_BCORR:
                reg[inst.target] |= reg[inst.source];
                break;
            case VM_OP_BXORI:
                reg[inst.target] ^= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_BXORR:
                reg[inst.target] ^= reg[inst.source];
                break;
            case VM_OP_BSHLI:
                reg[inst.target] <<= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_BSHLR:
                reg[inst.target] <<= reg[inst.source];
                break;
            case VM_OP_BSHRI:
                reg[inst.target] <<= static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_BSHRR:
                reg[inst.target] <<= reg[inst.source];
                break;
            case VM_OP_BSHSI:
                reg[inst.target] = static_cast<int64_t>(reg[inst.target]) >> static_cast<uint32_t>(inst.immval);
                break;
            case VM_OP_BSHSR:
                reg[inst.target] = static_cast<int64_t>(reg[inst.target]) >> reg[inst.source];
                break;

            //! 浮点数运算
            case VM_OP_FADD:
                reg_real[inst.target] += reg_real[inst.source];
                break;
            case VM_OP_FSUB:
                reg_real[inst.target] -= reg_real[inst.source];
                break;
            case VM_OP_FMUL:
                reg_real[inst.target] *= reg_real[inst.source];
                break;
            case VM_OP_FDIV:
                reg_real[inst.target] /= reg_real[inst.source];
                break;
            case VM_OP_FMOD:
                reg_real[inst.target] = fmod(reg_real[inst.target], reg_real[inst.source]);
                break;
            case VM_OP_FEQ:
                reg[inst.target] = (reg_real[inst.target] == reg_real[inst.source]) ? 1 : 0;
                break;
            case VM_OP_FNE:
                reg[inst.target] = (reg_real[inst.target] != reg_real[inst.source]) ? 1 : 0;
                break;
            case VM_OP_FGT:
                reg[inst.target] = (reg_real[inst.target] > reg_real[inst.source]) ? 1 : 0;
                break;
            case VM_OP_FGE:
                reg[inst.target] = (reg_real[inst.target] >= reg_real[inst.source]) ? 1 : 0;
                break;
            case VM_OP_FLT:
                reg[inst.target] = (reg_real[inst.target] < reg_real[inst.source]) ? 1 : 0;
                break;
            case VM_OP_FLE:
                reg[inst.target] = (reg_real[inst.target] <= reg_real[inst.source]) ? 1 : 0;
                break;

            //! 寄存器间数据交换
            case VM_OP_MOVE:
                reg[inst.target] = reg[inst.source];
                reg[inst.source] = 0;
                break;
            case VM_OP_COPY:
                reg[inst.target] ^= reg[inst.source];
                break;
            case VM_OP_SWAP:
                reg[inst.target] ^= reg[inst.source];
                reg[inst.source] ^= reg[inst.target];
                reg[inst.target] ^= reg[inst.source];
                break;
            case VM_OP_FILL:
                switch (inst.offset) {
                    case 1: // 按1bit位填充
                        reg[inst.target] = ((inst.immval & 0x01) ? (~static_cast<uint64_t>(0)) : static_cast<uint64_t>(0));
                        break;
                    case 2: // 按2bit位填充
                        reg[inst.target] = 0
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 0)  //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 2)  //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 4)  //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 6)  //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 8)  //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 10) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 12) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 14) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 16) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 18) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 20) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 22) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 24) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 26) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 28) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 30) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 32) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 34) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 36) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 38) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 40) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 42) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 44) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 46) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 48) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 50) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 52) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 54) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 56) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 58) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 60) //
                            | (static_cast<uint64_t>(inst.immval & 0x03) << 62) //
                            ;
                        break;
                    case 4: // 按1bit位填充
                        reg[inst.target] = 0
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 0)  //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 4)  //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 8)  //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 12) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 16) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 20) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 24) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 28) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 32) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 36) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 40) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 44) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 48) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 52) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 56) //
                            | (static_cast<uint64_t>(inst.immval & 0x0F) << 60) //
                            ;
                        break;
                    case 8: // 按1字节填充
                        reg[inst.target] = 0
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 0)  //
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 8)  //
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 16) //
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 24) //
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 32) //
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 40) //
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 48) //
                            | (static_cast<uint64_t>(inst.immval & 0xFF) << 56) //
                            ;
                        break;
                    case 16: // 按2字节填充
                        reg[inst.target] = 0
                            | (static_cast<uint64_t>(inst.immval & 0xFFFF) << 0)  //
                            | (static_cast<uint64_t>(inst.immval & 0xFFFF) << 16) //
                            | (static_cast<uint64_t>(inst.immval & 0xFFFF) << 32) //
                            | (static_cast<uint64_t>(inst.immval & 0xFFFF) << 48) //
                            ;
                        break;
                    case 32: // 按4字节填充
                        reg[inst.target] = 0
                            | (static_cast<uint64_t>(inst.immval & 0xFFFFFFFF) << 0)  //
                            | (static_cast<uint64_t>(inst.immval & 0xFFFFFFFF) << 32) //
                            ;
                        break;
                    default:
                        ASSERT(false);
                        break;
                }
                break;
            case VM_OP_SETB:
                reg[inst.target] = (static_cast<uint64_t>(inst.immval) & 0xFF);
                break;
            case VM_OP_SETH:
                reg[inst.target] = (static_cast<uint64_t>(inst.immval) & 0xFFFF);
                break;
            case VM_OP_SETW:
                reg[inst.target] = (static_cast<uint64_t>(inst.immval) & 0xFFFFFFFF);
                break;
            case VM_OP_SETD:
                reg[inst.target] = vm_fetch_dword(vm, pc++);
                break;

            //! 数据转换运算
            case VM_OP_BNOT:
                reg[inst.target] = ~reg[inst.target];
                break;
            case VM_OP_INEG:
                reg[inst.target] = ~reg[inst.target] + 1;
                break;
            case VM_OP_FNEG:
                reg_real[inst.target] = -reg_real[inst.target];
                break;
            case VM_OP_ITOF:
                ASSERT(false);
                break;
            case VM_OP_FTOI:
                ASSERT(false);
                break;
            case VM_OP_SBEX:
                reg[inst.target] = static_cast<int64_t>(static_cast<int8_t>(reg[inst.target] & 0xFF));
                break;
            case VM_OP_SHEX:
                reg[inst.target] = static_cast<int64_t>(static_cast<int16_t>(reg[inst.target] & 0xFFFF));
                break;
            case VM_OP_SWEX:
                reg[inst.target] = static_cast<int64_t>(static_cast<int32_t>(reg[inst.target] & 0xFFFFFFFF));
                break;
            //! 跳转指令
            case VM_OP_FAULT:
                return inst.immval;
            case VM_OP_RETURN:
                if (frame <= vm->frames) {
                    *result = reg[0];
                    return 0;
                }

                pc = vm_frame_load(vm, frame--);
                break;
            case VM_OP_CALL:
                vm_frame_save(vm, frame++, pc);
                pc += inst.immval;
                break;
            case VM_OP_JEZ:
                if (reg[inst.target] == 0) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JNZ:
                if (reg[inst.target] == 0) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JMP:
                pc = reg[inst.target] + inst.offset;
                break;

            //! 基于整数比较的跳转指令
            case VM_OP_JEQ:
                if (reg[inst.target] == reg[inst.source]) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JNE:
                if (reg[inst.target] != reg[inst.source]) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JGT:
                if (reg[inst.target] > reg[inst.source]) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JGE:
                if (reg[inst.target] >= reg[inst.source]) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JLT:
                if (reg[inst.target] < reg[inst.source]) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JLE:
                if (reg[inst.target] <= reg[inst.source]) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JET:
                if (reg[inst.target] & reg[inst.source]) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JSGT:
                if (static_cast<int64_t>(reg[inst.target]) > static_cast<int64_t>(reg[inst.source])) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JSGE:
                if (static_cast<int64_t>(reg[inst.target]) >= static_cast<int64_t>(reg[inst.source])) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JSLT:
                if (static_cast<int64_t>(reg[inst.target]) < static_cast<int64_t>(reg[inst.source])) {
                    pc += inst.offset;
                }
                break;
            case VM_OP_JSLE:
                if (static_cast<int64_t>(reg[inst.target]) <= static_cast<int64_t>(reg[inst.source])) {
                    pc += inst.offset;
                }
            break;

            default:
                break;

        }
    }
}